package verbindung;

import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.log4j.Logger;

/**
 * 
 * @author Nilientes
 * 
 */
public class DBDClientBin extends Thread implements DBDClient {

	/**
	 * 
	 */
	public static final byte DBD_WriteDatabase = 0;
	/**
	 * 
	 */
	public static final byte DBD_EndOfMessage = 1;
	/**
	 * 
	 */
	public static final byte DBD_EndOfSession = 2;
	/**
	 * 
	 */
	public static final byte DBD_DumpDatabase = 3;
	/**
	 * 
	 */
	public static final byte DBD_SubscribeDatabase = 4;
	/**
	 * 
	 */
	public static final byte DBD_QueryDatabase = 5;
	/**
	 * 
	 */

	private Socket dbdsocket;
	private DataOutputStream out;
	private DataInputStream in;
	private Logger logger;

	private BufferedWriter logdata;

	private HashMap<String, Integer> values;
	private HashMap<String, ArrayList<DBDCallback>> callbacks;

	protected DBDClientBin(String server, int port) {
		logger = Logger.getLogger(DBDClientBin.class);

		/*try {
			logdata = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("serverlogs\\log" + System.currentTimeMillis() + ".txt")));
		} catch (Exception e) {
			System.out.println(e);
		}*/
		logger.info("Connecting to server " + server + ":" + port);
		try {
			dbdsocket = new Socket(server, port);
			out = new DataOutputStream(dbdsocket.getOutputStream());
			in = new DataInputStream(dbdsocket.getInputStream());

			callbacks = new HashMap<String, ArrayList<DBDCallback>>();
			values = new HashMap<String, Integer>();

			this.start();
		} catch (Exception e) {
			logger.error("Can not connect to server " + server + " on port " + port);
		}
	}

	public void close()
	{
		logger.info("Closing server connection");

		/*try {
			logdata.flush();
			logdata.close();
		} catch (IOException e) {
			System.out.println(e);
		}*/

		try {
			in.close();
			out.close();
			dbdsocket.close();

		} catch (IOException e) {
			logger.error("close: Error while closing server connection");
		}
	}

	public int getVar(String name)
	{
		logger.info("getting " + name);
		DBDTransStruct tr = new DBDTransStruct(name, 0, DBDClientBin.DBD_QueryDatabase);

		try {
			tr.sendStruct(out);

		} catch (Exception e) {
			logger.error("getting the var " + name + " failed", e);
		}
		Integer inte;

		// waiting for the return
		while ((inte = values.get(name.toUpperCase())) == null)
			;

		return inte.intValue();
	}

	public void setVar(String name, int val)
	{
		if (name.equals("")) return;

		logger.info("setting " + name + " to " + val);
		DBDTransStruct tr = new DBDTransStruct(name, val, DBDClientBin.DBD_WriteDatabase);

		/*try {
			logdata.write("S " + name + " " + val);
			logdata.newLine();
		} catch (IOException e1) {
			e1.printStackTrace();
		}*/

		try {
			tr.sendStruct(out);
		} catch (IOException e) {
			logger.error("setting the var " + name + " failed", e);
		}

	}

	public DBDCallback subscribeVar(String name, DBDCallback cb)
	{
		logger.info("subscribing " + name);
		DBDTransStruct tr = new DBDTransStruct(name, 0, DBDClientBin.DBD_SubscribeDatabase);

		try {
			tr.sendStruct(out);
		} catch (IOException e) {
			logger.error("subscribing the var " + name + " failed", e);
		}

		ArrayList<DBDCallback> list;
		if ((list = callbacks.get(name)) == null) list = new ArrayList<DBDCallback>();

		list.add(cb);
		callbacks.put(name.toUpperCase(), list);

		return cb;
	}

	public void unsubscribeVar(String name)
	{
		values.remove(name);
		callbacks.remove(name);

		// todo: send some unsubscribe to server?
	}

	public void run()
	{
		DBDTransStruct t = new DBDTransStruct();

		while (true) {
			try {
				t.receiveStruct(in);
			} catch (Exception e) {
				logger.error("Server connection lost", e);
				break;
			}
			if (t.command == 0) {
				logger.info("writing to maps: " + t);
				values.put(t.name, new Integer(t.value));

				if (logdata != null) {
					try {
						logdata.write("R " + t.name + " " + t.value);
						logdata.newLine();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				if (callbacks.get(t.name) != null) for (DBDCallback call : callbacks.get(t.name))

					if (call != null) call.call(t.name, t.value);

			}

		}
	}

}
